//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "ipcchannel.h"
#include <aura.h>

#define Lock(hCritical)     do { \
                                AuraCriticalSection_Enter(hCritical); \
                            } while(0)

#define Unlock(hCritical)   do { \
                                AuraCriticalSection_Leave(hCritical); \
                            } while(0)

CChannel::~CChannel()
{
    if (INVALID_CHANNEL_ID != m_channelId) {
        AuraIpcChannel_Disconnect(m_channelId);
        m_channelId = INVALID_CHANNEL_ID;
    }
}

ECode CChannel::Initialize(
        /*[in] */ WString name)
{
    assert(!name.IsNullOrEmpty());
    return (ECode)AuraIpcChannel_Connect((Aura_PWChar)(const wchar_t *)name, &m_channelId);
}

ECode CChannel::Write(
        /* [in] */ PVoid pBuffer,
        /* [in] */ UInt32 bufferSize)
{
    return (ECode)AuraIpcChannel_AsyncWrite(m_channelId, pBuffer, (Aura_UInt32)bufferSize);
}

ECode CChannel::Read(
        /* [out] */ PVoid pBuffer,
        /* [in] */ UInt32 bufferSize)
{
    return (ECode)AuraIpcChannel_Read(m_channelId, pBuffer, (Aura_UInt32)bufferSize);
}

ECode CChannel::WriteAndRead(
        /* [in] */ PVoid pInBuffer,
        /* [in] */ UInt32 inBufferSize,
        /* [out] */ PVoid pOutBuffer,
        /* [in] */ UInt32 outBufferSize)
{
    return (ECode)AuraIpcChannel_SyncWrite(m_channelId,
                         pInBuffer,
                         (Aura_UInt32)inBufferSize,
                         pOutBuffer,
                         (Aura_UInt32)outBufferSize);
}

ECode CChannel::Invoke(
        /* [in] */ InvokeFuncPTR invokeFunc,
        /* [in] */ PVoid pArgs,
        /* [out] */ ECode * pResult)
{
    return invokeFunc(m_channelId, pArgs, pResult);
}

// ---------------------------------------------------

CChannelPool::~CChannelPool()
{
    DestroyAllChannels();

    if (NULL != m_pName) {
        WStringBuf::Free(m_pName);
        m_pName = NULL;
    }
    if (NULL != m_hLock) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
    }
}

ECode CChannelPool::Initialize(
        /* [in] */ WString name)
{
    assert(!name.IsNullOrEmpty());
    Int32 length = name.GetLength(AURA_MAX_PATH);
    assert(length > 0);
    ECode ec = NOERROR;

    ec = AuraCriticalSection_Create(&m_hLock);
    if (FAILED(ec)) {
        return ec;
    }
    m_pName = WStringBuf::Alloc(length);
    if (NULL == m_pName) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
        return E_OUT_OF_MEMORY;
    }
    m_pName->Copy(name);

    return NOERROR;
}

ECode CChannelPool::Write(
        /* [in] */ PVoid pBuffer,
        /* [in] */ UInt32 bufferSize)
{
    CChannel * pChannel = NULL;
    ECode ec = NOERROR;

    ec = GetChannel(&pChannel);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = pChannel->Write(pBuffer, bufferSize);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    PutbackChannel(pChannel);

    return ec;

ErrorExit:
    if (NULL != pChannel) {
        DestroyChannel(pChannel);
        pChannel = NULL;
    }
    return ec;
}

ECode CChannelPool::Read(
        /* [out] */ PVoid pBuffer,
        /* [in] */ UInt32 bufferSize)
{
    CChannel * pChannel = NULL;
    ECode ec = NOERROR;

    ec = GetChannel(&pChannel);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = pChannel->Read(pBuffer, bufferSize);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    PutbackChannel(pChannel);

    return ec;

ErrorExit:
    if (NULL != pChannel) {
        DestroyChannel(pChannel);
        pChannel = NULL;
    }
    return ec;
}

ECode CChannelPool::WriteAndRead(
        /* [in] */ PVoid pInBuffer,
        /* [in] */ UInt32 inBufferSize,
        /* [out] */ PVoid pOutBuffer,
        /* [in] */ UInt32 outBufferSize)
{
    CChannel * pChannel = NULL;
    ECode ec = NOERROR;

    ec = GetChannel(&pChannel);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = pChannel->WriteAndRead(pInBuffer, inBufferSize,
                             pOutBuffer, outBufferSize);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    PutbackChannel(pChannel);

    return ec;

ErrorExit:
    if (NULL != pChannel) {
        DestroyChannel(pChannel);
        pChannel = NULL;
    }
    return ec;
}

ECode CChannelPool::Invoke(
        /* [in] */ InvokeFuncPTR invokeFunc,
        /* [in] */ PVoid pArgs,
        /* [out] */ ECode * pResult)
{
    CChannel * pChannel = NULL;
    ECode ec = NOERROR;

    assert(NULL != invokeFunc);

    ec = GetChannel(&pChannel);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = pChannel->Invoke(invokeFunc, pArgs, pResult);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    PutbackChannel(pChannel);

    return ec;

ErrorExit:
    if (NULL != pChannel) {
        DestroyChannel(pChannel);
        pChannel = NULL;
    }
    return ec;
}

ECode CChannelPool::GetChannel(
        /* [out] */ CChannel ** ppChannel)
{
    CChannel * pChannel = NULL;

    assert(NULL != ppChannel);
    Lock(m_hLock);
    if (!m_channelList.IsEmpty()) {
        pChannel = (CChannel *)m_channelList.Next();
        pChannel->Detach();
        m_channels--;
        Unlock(m_hLock);
        *ppChannel = pChannel;

        return NOERROR;
    }
    Unlock(m_hLock);

    return CreateChannel(ppChannel);
}

void CChannelPool::PutbackChannel(
        /* [in] */ CChannel * pChannel)
{
    assert(NULL != pChannel);
    Lock(m_hLock);
    if (m_channels < m_capacity) {
        m_channelList.InsertLast(pChannel);
        m_channels++;
        Unlock(m_hLock);

        return;
    }
    Unlock(m_hLock);
    DestroyChannel(pChannel);

    return ;
}

ECode CChannelPool::CreateChannel(
        /* [out] */ CChannel ** ppChannel)
{
    CChannel * pChannel = NULL;
    ECode ec = NOERROR;

    assert(NULL != ppChannel);
    pChannel = new CChannel();
    if (NULL == pChannel) {
        goto ErrorExit;
    }
    ec = pChannel->Initialize(*m_pName);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    *ppChannel = pChannel;

    return NOERROR;

ErrorExit:
    if (NULL != pChannel) {
        delete pChannel;
        pChannel = NULL;
    }
    return ec;
}

void CChannelPool::DestroyChannel(
        /* [in] */ CChannel * pChannel)
{
    assert(NULL != pChannel);
    delete pChannel;

    return ;
}

void CChannelPool::DestroyAllChannels()
{
    CChannel * pChannel = NULL;

    Lock(m_hLock);
    while(!m_channelList.IsEmpty()) {
        pChannel = (CChannel *)m_channelList.Next();
        pChannel->Detach();
        delete pChannel;
        pChannel = NULL;
    }
    Unlock(m_hLock);

    return ;
}

